package it.gtd.swimpool.model.business;

import com.google.common.annotations.VisibleForTesting;
import it.gtd.swimpool.model.entity.Administrator;
import it.gtd.swimpool.model.entity.User;

import javax.ejb.EJB;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.util.Calendar;
import java.util.Date;

import static com.google.common.base.Preconditions.*;

@Stateless
@LocalBean
public class AdministrationManager {
    @PersistenceContext
    private EntityManager entityManager;

    @EJB
    private UserManager userManager;

    @EJB
    private CommunicationManager communicationManager;

    private static final int idParameter = 1;

    @VisibleForTesting
    static final String promoteUserByIdQuery =
            "UPDATE users AS user SET user.role = 'admin' WHERE user.id = ?1";

    @VisibleForTesting
    static final String demoteAdminByIdQuery =
            "UPDATE users AS user SET user.role = 'user' WHERE user.id = ?1";


    private static final String WELCOME_TEXT = "BLABLA!";
    private static final String ACCOUNT_DELETED_TEXT = "BLABLA!";
    private static final int MIN_ADMIN_AGE = 20; // in days

    public AdministrationManager() {
    }

    @VisibleForTesting
    AdministrationManager(
            EntityManager entityManager,
            UserManager userManager,
            CommunicationManager communicationManager
    ) {
        this();

        checkNotNull(entityManager, "entityManager can't be null");
        checkNotNull(userManager, "userManager can't be null");
        checkNotNull(communicationManager, "communicationManager can't be null");

        this.entityManager = entityManager;
        this.userManager = userManager;
        this.communicationManager = communicationManager;
    }

    public void promote(User user) {
        checkNotNull(user, "user can't be null");
        checkArgument(entityManager.contains(user), "User " + user.getEmail() + " not managed entity");

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(Calendar.DATE, -MIN_ADMIN_AGE);

        Date deadline = calendar.getTime();

        checkState(user.getCreationDate().before(deadline), user.getName() + " is too young; you can't promote him to administrator yet");
        checkState(!user.isDemoted(),  user.getName() + " has been demoted; nobody can promote him");

        Query promoteUserById = entityManager.createNativeQuery(promoteUserByIdQuery);
        promoteUserById.setParameter(idParameter, user.getId());
        int result = promoteUserById.executeUpdate();

        checkState(result != 0, "No user with id " + user.getId());
        checkState(result == 1, "Multiple user with the same id " + user.getId());
    }

    public void demote(Administrator target) {
        checkNotNull(target, "target can't be null");
        checkArgument(entityManager.contains(target), "User " + target.getEmail() + " not managed entity");

        Query demoteAdminById = entityManager.createNativeQuery(demoteAdminByIdQuery);
        demoteAdminById.setParameter(idParameter, target.getId());
        int result = demoteAdminById.executeUpdate();

        target.demote();

        checkState(result != 0, "No user with id " + target.getId());
        checkState(result == 1, "Multiple user with the same id " + target.getId());
    }

    public void forceActivate(User account) {
        checkNotNull(account, "account can't be null");
        checkArgument(entityManager.contains(account), "User " + account.getEmail() + " not managed entity");

        account.activate();

        communicationManager.sendEmail(account, "Welcome!", WELCOME_TEXT);
    }

    public void deleteAccount(User account) {
        checkNotNull(account, "account can't be null");
        checkArgument(entityManager.contains(account), "User " + account.getEmail() + " not managed entity");

        entityManager.remove(account);

        checkState(!entityManager.contains(account), "User " + account.getEmail() + " is managed entity");

        communicationManager.sendEmail(account, "Account Deleted", ACCOUNT_DELETED_TEXT);
    }

    public void forcePasswordChange(User account, String newPassword) {
        checkNotNull(account, "account can't be null");
        checkNotNull(newPassword, "newPassword can't be null");
        checkArgument(entityManager.contains(account), "User " + account.getEmail() + " not managed entity");

        account.setPassword(newPassword);
    }

    public void forceEmailChange(User account, String newEmail) {
        checkNotNull(account, "account can't be null");
        checkNotNull(newEmail, "newEmail can't be null");
        checkArgument(entityManager.contains(account), "User " + account.getEmail() + " not managed entity");

        account.setEmail(newEmail);
    }
}
